﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Collision;
using FarseerPhysics.Factories;
using EpicBall.ScreenSystem;
using System.Diagnostics;

namespace EpicBall.Levels.Classes
{
    public class MovingPlatform : LevelObject
    {
        /// <summary>
        /// Vertices that form a path for the
        /// moving platform to follow
        /// </summary>
        public struct AIPathNode
        {
            public Vector2 _vertex;
            public Vector2 vertex { get { return _vertex; } }
            public bool _pauseUponReaching;
            public float _pauseDuration;
            public float _speed;
            public AIPathNode(Vector2 vertex) : this(vertex, 100) { }
            public AIPathNode(Vector2 vertex, float speed) : this(vertex, speed, false, 0) { }
            public AIPathNode(Vector2 vertex, float speed, bool pauseUponReaching, float pauseDuration) {
                _pauseUponReaching = pauseUponReaching;
                _pauseDuration = pauseDuration;
                _vertex = ConvertUnits.ToSimUnits(vertex);
                _speed = ConvertUnits.ToSimUnits(speed);
            }
        }

        //Are we iterating forwards through the vertices or are we in reverse
        public enum Direction
        {
            forward,
            backward,
        }

        protected List<AIPathNode> aipath;          //Keep track of all vertices on our path
        protected int approachingCurrentNode = 1;
        public bool reverseUponDestination = true;
        protected bool isPaused = false;
        protected Direction direction = Direction.forward;
        float pauseElapsed = 0;

        public MovingPlatform(PhysicsGameScreen screen, Texture2D texture) : base(screen, texture) { aipath = new List<AIPathNode>(); }
        public MovingPlatform(PhysicsGameScreen screen, int width, int height) : base(screen,  screen.GameStack.Content.Load<Texture2D>("Enemies/projectilePlatform"), width, height) { aipath = new List<AIPathNode>(); }
        public MovingPlatform(PhysicsGameScreen screen, Texture2D texture, int width, int height) : base (screen, texture, width, height) { aipath = new List<AIPathNode>(); }
        public MovingPlatform(PhysicsGameScreen screen, Texture2D texture, Body body) : base(screen, texture, body ) { aipath = new List<AIPathNode>(); }
        public MovingPlatform(PhysicsGameScreen screen, Texture2D texture, Body body, int width, int height) : base(screen, texture, body, width, height) { aipath = new List<AIPathNode>(); }

        /// <summary>
        /// Sets the starting position of the moving platform
        /// </summary>
        public void Ready()
        {
            _body.Position = aipath[0].vertex;
        }

        public void AddPathNode(AIPathNode pathnode)
        {
            aipath.Add(pathnode);
        }


        public override void Update(GameTime gameTime)
        {
            AIPathNode approachingNode = aipath[approachingCurrentNode];
            if (!isPaused)  //Check to make sure it is not currently paused at a node
            {
                Vector2 approachPos = approachingNode.vertex;
                Vector2 bodyPos = _body.Position;
                float deltaY = approachPos.Y - bodyPos.Y;
                float deltaX = approachPos.X - bodyPos.X;
                float speedX, speedY, stepSpeed;
                speedX = speedY = stepSpeed = approachingNode._speed * gameTime.ElapsedGameTime.Milliseconds/1000;
                bool arrived = false;

                //Set the correct increments of speed
                if (deltaX != 0 && deltaY != 0)
                {
                    double angle = Math.Atan(deltaY / deltaX);
                    speedX = Math.Abs((float)(Math.Cos(angle) * stepSpeed));
                    speedY = Math.Abs((float)(Math.Sin(angle) * stepSpeed));
                }

                if (deltaX == 0) speedX = 0;
                if (deltaY == 0) speedY = 0;

                //We are moving to the right
                if (deltaX > 0 && (bodyPos.X + speedX) >= approachPos.X) arrived = true;
                
                //we are moving to the left
                if (deltaX < 0)
                {
                    if((bodyPos.X - speedX) <= approachPos.X) arrived = true;
                    else speedX = -speedX;
                }

                //We are moving downwards
                if (deltaY > 0 && (bodyPos.Y + speedY) >= approachPos.Y) arrived = true;

                //We are moving upwards
                if (deltaY < 0)
                {
                    if((bodyPos.Y - speedY) <= approachPos.Y) arrived = true;
                    else speedY = -speedY;
                }

                //We have arrived at our node
                if (arrived)
                {
                    _body.Position = approachingNode.vertex;

                    //Check to see if the current node has a pause duration set
                    if (approachingNode._pauseUponReaching)
                    {
                        isPaused = true;
                    }
                    else
                    {
                        NextNode();
                    }
                }
                else
                {
                    _body.Position = new Vector2(bodyPos.X + speedX, bodyPos.Y + speedY);
                }
            }
            else //we are currently paused
            {
                //Make sure pause duration has been fully met before continuing
                if (pauseElapsed > approachingNode._pauseDuration)
                {
                    isPaused = false;
                    pauseElapsed = 0;
                    NextNode();
                }
                else
                {
                    pauseElapsed += gameTime.ElapsedGameTime.Milliseconds;
                }
            }
            base.Update(gameTime);
        }

        protected void NextNode()
        {
            if (direction == Direction.forward)
            {
                //We've reached the end of our forward destination
                if ((approachingCurrentNode + 1) == aipath.Count)
                {
                    //Check to see if the platform is set to go backwards
                    if (reverseUponDestination)
                    {
                        direction = Direction.backward;
                        --approachingCurrentNode;
                    }
                }
                else ++approachingCurrentNode;
            }
            else
            {
                if ((approachingCurrentNode - 1) < 0)
                {
                    direction = Direction.forward;
                    ++approachingCurrentNode;
                }
                else --approachingCurrentNode;
            }
        }
    }
}
